package controller;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.TreeMap;
import jgf.IGraph;
import jgf.lists.Digraph;
import jgf.lists.Graph;
import jgf.lists.Multidigraph;
import jgf.lists.Multigraph;
import jgf.lists.Pseudodigraph;
import jgf.lists.Pseudograph;
import jgf.lists.Pseudomultidigraph;
import jgf.lists.Pseudomultigraph;
import model.observer.JGSObservable;
import model.observer.JGSObserver;

/**
 * 
 * @author Marcos Garcia Ortega <a href="mailto:thiamath@gmail.com">&lt;thiamath@gmail.com&gt;</a>
 */
public class Controller {

	/** A map with all the graphs in the model. */
	protected TreeMap<Integer, JGSObservable> model;
	/** A counter to assign univoque IDs to the new graphs. */
	protected Integer graphIDCounter = 0;
	
	// <editor-fold defaultstate="collapsed" desc="Singleton Code">
	/** The instance of the controller. */
	private static Controller instance = null;

	/**
	 * The constructor initializes the map and the counter.
	 */
	private Controller() {
		graphIDCounter = 0;
		model = new TreeMap<Integer, JGSObservable>();
	}

	/**
	 * The getter of the instance of the controller.
	 * @return The instance of the controller.
	 */
	public static Controller getInstance() {
		if (instance == null) {
			instance = new Controller();
		}
		return instance;
	}
	// </editor-fold>

	/**
	 * Adds the graph into the model.
	 * @param graph The graph that is going to be inserted into the system.
	 */
	private void addGraph(JGSObserver observer, IGraph graph) {
		
		JGSObservable observable = new JGSObservable();
		
		observable.addObserver(observer);
		
		graph.setID(graphIDCounter++);
		
		model.put(graph.getID(), observable);

		observable.loadGraph(graph);
	}

	/**
	 * Removes the graph from the sistem (but is not deleted) only if theres not
	 * observers over it.
	 * @param graphID The ID of the graph to remove.
	 */
	private void removeGraph(Integer graphID) {
		JGSObservable observable = model.get(graphID);

		if(observable.hasObservers()){

		} else {
			model.remove(graphID);
		}
	}

	/**
	 * TODO comentar public void newGraph(JGSObserver observer, int type)
	 * @param observer
	 * @param type
	 */
	public void newGraph(JGSObserver observer, int type){
		IGraph graph = null;
		switch(type){
			case IGraph.GRAPH:
				graph = new Graph();
				break;
			case IGraph.GRAPH + IGraph.MULTI:
				graph = new Multigraph();
				break;
			case IGraph.GRAPH + IGraph.PSEUDO:
				graph = new Pseudograph();
				break;
			case IGraph.GRAPH + IGraph.MULTI + IGraph.PSEUDO:
				graph = new Pseudomultigraph();
				break;
			case IGraph.DIGRAPH:
				graph = new Digraph();
				break;
			case IGraph.DIGRAPH + IGraph.MULTI:
				graph = new Multidigraph();
				break;
			case IGraph.DIGRAPH + IGraph.PSEUDO:
				graph = new Pseudodigraph();
				break;
			case IGraph.DIGRAPH + IGraph.MULTI + IGraph.PSEUDO:
				graph = new Pseudomultidigraph();
				break;
		}
		addGraph(observer, graph);
	}

	/**
	 * Saves the graph as a file.
	 * @param graphID The ID of the graph to save.
	 * @param path The path where the graph is going to be saved.
	 * @throws IOException If theres any problem saving the graph,
	 *		this exception will raise.
	 */
	public void saveGraphToFile(Integer graphID, String path)
			throws IOException {
		//PENDING Hay que añadir clausulas de seguridad.
		//FIXME El archivo no se guarda en el directorio que indica la ventana de guardado.
		File a = new File(path);
		FileOutputStream out = new FileOutputStream(a);
		ObjectOutputStream salida = new ObjectOutputStream(out);

		IGraph g = model.get(graphID).getGraphInstance();

		salida.writeObject(g);
		salida.close();
		out.close();
	}

	/**
	 * Loads the graph from a file and adds it to the system.
	 * @param path The path of the graph.
	 * @throws IOException If there is any problem with the file system,
	 *		this exception will raise.
	 */
	public void loadGraphFromFile(JGSObserver observer, String path)
			throws IOException {
		ObjectInputStream entrada = new ObjectInputStream(new FileInputStream(new File(path)));

		try {
			IGraph ret = (IGraph) entrada.readObject();
			addGraph(observer, ret);
		} catch (ClassNotFoundException e) {
			//TODO add exception handling code
		}

		entrada.close();
	}

	/**
	 * Indicates to the controller that the view has closed the graph.
	 * @param graphID An <code>Integer</code> with the ID of the graph.
	 */
	public void closeGraph(Integer graphID){
		removeGraph(graphID);
	}

	/**
	 * Returns an instancce of a graph controller linked with the given graph.
	 * @param graphID The ID of the graph to manage.
	 * @return The instance of the GraphController.
	 */
	public GraphController getGraphController(Integer graphID) {
		return new GraphController(model.get(graphID));
	}
}

